package com.woniuxy.juc.cas;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntUnaryOperator;

/**
 * <P> @Author: ideaAdmin
 * <P> TODO: Class
 * <P> TODO: 模拟银行取钱
 * <P> Date: 2022/4/9:9:04
 * <P> Description:
 */

public class AccountMoney {

    public static void main(String[] args) {

        // Account account = new AccountUnsafe(10000);
        //Account.demo(account);
        AccountSafe account1 = new AccountSafe(10000);
        Account.demo(account1);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class AccountSafe implements Account {

        private AtomicInteger balance;

        public AccountSafe(int balance) {
            this.balance = new AtomicInteger(balance);
        }

        @Override
        public Integer getBalance() {
            return this.balance.get();
        }

        @Override
        public void withdraw(Integer amount) {

//            while (true) {
//                int prev = balance.get();
//
//                int next = prev - amount;
//
//                if (balance.compareAndSet(prev, next)) {
//                    break;
//                }
//            }
            balance.addAndGet(-1 * amount);
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class AccountUnsafe implements Account {


        private Integer balance;

        @Override
        public Integer getBalance() {
            synchronized (this) {
                return this.balance;
            }
        }

        @Override
        public void withdraw(Integer amount) {
            synchronized (this) {
                this.balance -= amount;
            }
        }
    }

    interface Account {
        // 获取余额
        Integer getBalance();

        // 取款
        void withdraw(Integer amount);

        /**
         * 方法内会启动 1000 个线程，每个线程做 -10 元 的操作
         * 如果初始余额为 10000 那么正确的结果应当是 0
         */
        static void demo(Account account) {
            List<Thread> ts = new ArrayList<>();
            long start = System.nanoTime();
            for (int i = 0; i < 1000; i++) {
                ts.add(new Thread(() -> {
                    account.withdraw(10);
                }));
            }
            ts.forEach(Thread::start);
            ts.forEach(t -> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            long end = System.nanoTime();
            System.out.println(account.getBalance()
                    + " cost: " + (end - start) / 1000_000 + " ms");
        }
    }

}
